package cn.xorange.commons.utils.spring;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.FieldInfo;

import javassist.bytecode.annotation.*;

import java.util.*;

/**
 * @author : yangjian
 * date : 2024/12/11
 * description :
 */
public class AnnotationUtils {
    private AnnotationUtils(){}


    /**
     *
     * @param clazz 类
     * @param fieldName 类属性
     * @return FieldInfo
     */
    public static FieldInfo getFieldInfo(Class<?> clazz, String fieldName){
        try {
            String className = clazz.getName();
            //默认的类搜索路径
            ClassPool pool = ClassPool.getDefault();
            //获取一个ctClass对象 com.example.demo.excel.entity.CitiesVo 这个是包的相对路径
            CtClass ctClass = pool.get(className);
            //获取这个字段
            CtField ctField = ctClass.getField(fieldName);
            return ctField.getFieldInfo();
        }catch (NotFoundException e){
            System.out.println("此类不存在" + e);
            return null;
        }
    }


    /**
     * 功能：动态的给类属性添加注解
     *  用于本来没有注解，直接添加上一行整注解
     * （可以持久化到内存，可作为工具类使用）
     * @param typeName  注解名 com.example.demo.excel.annotation.Excel
     */
    public static void addAnnotation(FieldInfo fieldInfo, String typeName, Map<String, MemberValue> memberValues) {
        ConstPool cp = fieldInfo.getConstPool();
        AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
        // Annotation 默认构造方法  typeName:表示的是注解的路径
        Annotation annotation = new Annotation(typeName, cp);
        // name 表示的是自定义注解的 方法  new StringMemberValue("名字", cp) 表示给name赋值
        if(Objects.nonNull(memberValues)){
            for (String k : memberValues.keySet()){
                annotation.addMemberValue(k, memberValues.get(k));
            }
        }
        attribute.addAnnotation(annotation);
        fieldInfo.addAttribute(attribute);
        System.out.println("添加后的所有注解" + Arrays.toString(attribute.getAnnotations()));
    }



    /**
     * 功能：动态的给类属性添加注解
     *  用于本来没有注解，直接添加上一行整注解
     * （可以持久化到内存，可作为工具类使用）
     * @param className     类名
     * @param attributeName 类属性
     * @param typeName      注解类型
     */
    public static void addAnnotation(String className, String attributeName, String typeName) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ct = pool.get(className);
            CtField cf = ct.getField(attributeName);
            FieldInfo fieldInfo = cf.getFieldInfo();
            AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
            ConstPool cp = fieldInfo.getConstPool();
            Annotation annotation = new Annotation(typeName, cp);
            System.out.println("添加注解" + annotation);
            attribute.addAnnotation(annotation);
            System.out.println("添加后的所有注解" + Arrays.toString(attribute.getAnnotations()));
        } catch (NotFoundException e) {
            System.out.println("此类不存在" + e);
        }
    }

    /**
     * 功能：使用生成的注解体 ；动态的给类注解添加属性
     * （可以持久化到内存，可作为工具类使用）
     * @param className     类名
     * @param attributeName 类属性
     */
    public static void addAnnotationBy(String className, String attributeName, Annotation annotation) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ct = pool.get(className);
            CtField cf = ct.getField(attributeName);
            FieldInfo fieldInfo = cf.getFieldInfo();
            AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
            attribute.addAnnotation(annotation);
            System.out.println("添加后的所有注解" + Arrays.toString(attribute.getAnnotations()));
        } catch (NotFoundException e) {
            System.out.println("此类不存在" + e);
        }
    }

    /**
     * 遍历注解所有属性
     *
     * @param className     类名
     * @param attributeName 类属性
     */
    public static void queryAnnotation(String className, String attributeName) {
        System.out.println("====开始遍历注解所有属性=====");
        try {
            ClassPool pool = ClassPool.getDefault();
            //获取实体类
            CtClass ct = pool.get(className);
            //获取属性
            CtField cf = ct.getField(attributeName);
            //获取属性字段信息
            FieldInfo fieldInfo = cf.getFieldInfo();
            //获取属性字段的运行时可见注释
            AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
            //获取所有注解
            Annotation[] annotations = attribute.getAnnotations();
            int sum = 0;
            //遍历注解
            for (Annotation annotation : annotations) {
                sum++;
                System.out.println("注解" + sum + "：" + annotation.toString());
                //如果没有属性名，就下一个循环
                if (annotation.getMemberNames() == null) {
                    System.out.println("！无属性名跟属性值！");
                    continue;
                }
                //获取注解的所有属性名，跟属性值
                for (Object memberName : annotation.getMemberNames()) {
                    MemberValue memberValue = annotation.getMemberValue(String.valueOf(memberName));
                    System.out.println("获取到的注解的属性名：" + memberName);
                    System.out.println("获取到的注解的属性值：" + memberValue);
                }
            }
        } catch (NotFoundException e) {
            System.out.println("此类不存在" + e);
        }
    }

    /**
     * 给字段注解添加属性，添加单一注解的属性
     * （不能持久化到内存，只能作为代码一部分使用）
     * @param className     类名
     * @param attributeName 类属性
     * @param name          指定注解名
     * @param key           给注解添加的属性名
     * @param value         给注解添加的属性的值
     */
    public static void editAnnotation(String className, String attributeName, String name, String key, String value) {
        try {
            ClassPool pool = ClassPool.getDefault();
            //获取实体类
            CtClass ct = pool.get(className);
            //获取属性
            CtField cf = ct.getField(attributeName);
            //获取属性字段信息
            FieldInfo fieldInfo = cf.getFieldInfo();
            //获取该字段的常量池
            ConstPool constPool = cf.getFieldInfo().getConstPool();
            //获取属性字段的运行时可见注释
            AnnotationsAttribute attribute = (AnnotationsAttribute) fieldInfo.getAttribute(AnnotationsAttribute.visibleTag);
            //获取所有注解
            Annotation[] annotations = attribute.getAnnotations();
            for (Annotation annotation : annotations) {
                //如果没有属性名，就下一个循环
                if (annotation.getMemberNames() == null) {
                    continue;
                }
                if (annotation.toString().contains(name)) {
                    System.out.println("添加属性");
                    //给指定的注解添加属性
                    annotation.addMemberValue(key, new StringMemberValue(value, constPool));
                }
            }

            //遍历检查是否添加上注解
            for (Annotation annotation : annotations) {
                Set<String> memberNames = annotation.getMemberNames();
                if (annotation.getMemberNames() == null) {
                    System.out.println("！无属性名跟属性值！");
                    continue;
                }
                for (String memberName : memberNames) {
                    System.out.println("名称：" + memberName + "值：" + annotation.getMemberValue(memberName));
                }
            }
        } catch (Exception e) {
            System.out.println("捕获异常" + e.getMessage());
        }
    }

}
